<template>
    <div class="generic-type-specify" v-if="clazz?.declaredGenericTypes?.length">
        <div class="caption overflow-ellipsis" :title="joinCaption(displayTypes,false)">
            {{ joinCaption(displayTypes, true) }}
        </div>
        <tnxel-button class="ms-1 px-2"
            :type="updateButtonType"
            text
            bg
            icon="MoreFilled"
            @click="toUpdate"
            v-if="updatable"/>
        <tnxel-dialog v-model="dialog.visible"
            :buttons="dialog.buttons"
            :title="title"
            :width="500"
        >
            <h4 class="mt-0 text-center">{{ clazz.getName() + joinCaption(clazz.declaredGenericTypes) }}</h4>
            <tnxel-edit-table :data="types"
                :addable="false"
                :removable="false">
                <el-table-column label="序号" type="index" align="center" width="60"/>
                <el-table-column label="参数" prop="parameter" width="60">
                    <template #default="{row}">
                        <tnxel-select v-model="row.parameter"
                            :items="parameterItems"
                            empty
                            placeholder=""
                            @change="onParameterChange(row)"
                        />
                    </template>
                </el-table-column>
                <el-table-column label="限定" prop="extended" width="100">
                    <template #default="{row}">
                        <tnxel-select v-model="row.extended"
                            :items="extendedItems"
                            empty
                            placeholder=""
                            @change="onExtendedChange(row)"
                        />
                    </template>
                </el-table-column>
                <el-table-column label="类型" prop="typeId">
                    <template #default="{row}">
                        <LogicTypeSelect v-model="row.typeId"
                            :app="app"
                            :kind="TypeKind.GENERIC"
                            :disabled="!!(row.parameter && (row.extended === '' || row.extended === null))"
                            @change="onTypeIdChange(row)"
                        />
                    </template>
                </el-table-column>
                <el-table-column label="数组" prop="arrayed" align="center" width="60">
                    <template #default="{ row }">
                        <ArrayedSelect v-model="row.arrayed" :property="row"/>
                    </template>
                </el-table-column>
            </tnxel-edit-table>
        </tnxel-dialog>
    </div>
</template>

<script>
import tnx from '@/tnx.js';
import ModelingApp from '@/logic/modeling/entity/ModelingApp.js';
import DependencyClass from '@/logic/modeling/entity/DependencyClass.js';
import TypeKind from '@/logic/modeling/enums/TypeKind.js';
import GenericType from '@/logic/modeling/entity/GenericType.js';
import GenerateUtil from '@/logic/modeling/util/generate.js';

import LogicTypeSelect from './LogicTypeSelect.vue';
import ArrayedSelect from "@/renderer/pages/modeling/components/ArrayedSelect.vue";

export default {
    name: 'GenericTypeSpecify',
    components: {ArrayedSelect, LogicTypeSelect},
    props: {
        modelValue: {
            type: Array,
            default: () => [],
        },
        app: {
            type: ModelingApp,
            required: true,
        },
        logicType: String,
        updatable: {
            type: Boolean,
            default: true,
        },
        updateButtonType: String,
        title: {
            type: String,
            default: '泛型设定',
        },
    },
    emits: ['update:modelValue', 'change'],
    data() {
        return {
            TypeKind: TypeKind,
            types: [],
            clazz: null,
            dialog: {
                visible: false,
                buttons: tnx.getDefaultDialogButtons('confirm', this.toSubmitUpdate),
            },
        };
    },
    computed: {
        parameterItems() {
            return [{
                text: '?',
                value: '?',
            }];
        },
        extendedItems() {
            return [{
                text: 'extends',
                value: 'true',
            }, {
                text: 'super',
                value: 'false',
            }];
        },
        displayTypes() {
            let displayTypes = [];
            if (this.clazz && this.clazz.declaredGenericTypes?.length) {
                for (let i = 0; i < this.clazz.declaredGenericTypes.length; i++) {
                    let displayType = this.clazz.declaredGenericTypes[i].clone();
                    let modelType = this.modelValue[i];
                    if (!modelType) {
                        if (this.clazz.defaultGenericTypes) {
                            modelType = this.clazz.defaultGenericTypes[i] || GenericType.of({
                                parameter: '?',
                            });
                        }
                    }
                    if (modelType) {
                        displayType.parameter = modelType.parameter;
                        displayType.arrayed = modelType.arrayed;
                        if (typeof modelType.extended === 'boolean') {
                            displayType.extended = modelType.extended;
                        }
                        displayType.typeId = modelType.typeId;
                        if (displayType.typeId) {
                            let refClass = this.app.findClass(displayType.typeId, true, true);
                            if (refClass) {
                                this.assignName(displayType, refClass);
                            }
                        }
                    }
                    displayTypes.push(displayType);
                }
            }
            return displayTypes;
        },
    },
    watch: {
        modelValue() {
            this.initTypes();
        },
        logicType() {
            this.init();
        },
    },
    mounted() {
        this.init();
        if (!this.modelValue.length) {
            this.updateModelValue();
        }
        tnx.app.eventBus.on('app-changed', this.onAppChanged);
    },
    unmounted() {
        tnx.app.eventBus.off('app-changed', this.onAppChanged);
    },
    methods: {
        onAppChanged(appName) {
            if (this.app.name === appName) {
                this.init();
            }
        },
        onParameterChange(row) {
            if (!row.parameter) {
                row.extended = '';
            }
        },
        onExtendedChange(row) {
            if (row.extended !== '') {
                row.parameter = '?';
            }
        },
        init() {
            if (this.logicType) {
                this.clazz = this.app.findClass(this.logicType, true);
                if (this.clazz instanceof DependencyClass) {
                    this.clazz = this.clazz.origin;
                }
            }
            this.initTypes();
        },
        initTypes() {
            this.types.length = 0;
            for (let displayType of this.displayTypes) {
                let type = displayType.clone();
                this.assignName(type, displayType);
                this.types.push(type);
            }
        },
        joinCaption(types, simple) {
            return GenerateUtil.generateGenericTypesCaption(this.app, types, simple);
        },
        assignName(targetType, sourceType) {
            targetType.packageName = sourceType.packageName || '';
            targetType.simpleName = sourceType.simpleName || '';
            targetType.name = sourceType.getName ? sourceType.getName() : (sourceType.name || '');
        },
        toUpdate() {
            this.dialog.visible = true;
        },
        onTypeIdChange(row) {
            if (row.typeId) {
                let refClass = this.app.findClass(row.typeId, true, true);
                if (refClass) {
                    this.assignName(row, refClass);
                    return;
                }
            }
            this.assignName(row, {});
        },
        updateModelValue() {
            let oldTypes = this.modelValue;
            this.$emit('update:modelValue', this.types.map(type => {
                let result = type.clone();
                if (result.extended === '') {
                    result.extended = null;
                }
                if (result.parameter && result.extended === null) {
                    result.typeId = '';
                }
                return result;
            }));
            this.$nextTick(() => {
                let newTypes = this.modelValue;
                if (!newTypes.equals(oldTypes, (type1, type2) => type1.equals(type2))) {
                    this.$emit('change', newTypes, oldTypes);
                }
            });
        },
        toSubmitUpdate(yes) {
            if (yes) {
                this.updateModelValue();
            }
        },
    }
}
</script>

<style lang="scss">
.generic-type-specify {
    width: 100%;
    display: flex;
    align-items: center;
    flex-wrap: nowrap;

    .caption {
        text-align: start;
        white-space: nowrap;
        cursor: default;
    }

    .el-button {
        border-width: 1px;
    }
}

.el-table .generic-type-specify .caption {
    flex-grow: 1;
}
</style>
